//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//

#include <windows.h>
#include <pm.h>
#include "Cs.h"
#include "Csmedia.h"

#include "CameraPDDProps.h"
#include "dstruct.h"
#include "dbgsettings.h"
#include <camera.h>
#include "CameraDriver.h"
#include "PinDriver.h"

#include "camerapdd.h"
#include "pdd_private.h"
#include "ov5623.h"
#include "xllp_camera_os_depend.h"

TCHAR *dev_name = L"CAM1";

void CCameraPdd::init_ipm_client()
{
    //set up IPM
    //TCHAR szTemp[MAX_PATH];
    IPM_STATUS_T status;
    is_ipm_enabled = FALSE;
    ipm_block_ref_count = 0;
    // IPM Registration:
    status = IPM_Register(dev_name, &(ipm_client_id) ,3000);
    if ( status==IPM_STATUS_SUCCESS)
    {
        DWORD dwProcState;
        NKDbgPrintfW (L"[%s] Registration Completed, client id is %d\r\n", dev_name, ipm_client_id);

        dwProcState = IPM_PROCESSOR_D0_RDY | IPM_PROCESSOR_RING_OSC_RDY  | IPM_PROCESSOR_D2_RDY| IPM_PROCESSOR_D1_RDY; // Initial

        // Notify IPM 
	NKDbgPrintfW(L"[%s] Initial Notify IPM Ready For Processor state. dwProcState=%x \r\n", dev_name, dwProcState);
	if (IPM_NotifyReadyForProcState(ipm_client_id, dwProcState ,3000) != IPM_STATUS_SUCCESS)
	    NKDbgPrintfW(L"[%s] Notify IPM Ready For Processor state for driver FAILED\r\n", dev_name);

        is_ipm_enabled = TRUE;
    }
    else if(status == IPM_STATUS_IPM_DISABLED)
        NKDbgPrintfW (L"[%s] NOIPM \r\n", dev_name);
    else
        NKDbgPrintfW (L"[%s] Registration Failed, IPM Error %d\r\n", dev_name, status);
}

void CCameraPdd::set_ipm_block(BOOL is_on)
{
    if (!is_ipm_enabled)
        return;

    if (is_on)
    {
        ipm_block_ref_count++;
        if (ipm_block_ref_count > 1)
            return;
    }
    else
    {
        if (ipm_block_ref_count)
        {
            ipm_block_ref_count--;
            if (ipm_block_ref_count)
                return;
        } else
            return;
    }

    NKDbgPrintfW(L"CCameraPdd::set_ipm_block %d, ref count %d!\r\n", is_on, ipm_block_ref_count);

    DWORD dwProcState;
    dwProcState = IPM_PROCESSOR_D0_RDY
                | IPM_PROCESSOR_RING_OSC_RDY  
                | IPM_PROCESSOR_D2_RDY
                | IPM_PROCESSOR_D1_RDY; // Initial

    if (ipm_block_ref_count)
        dwProcState = IPM_PROCESSOR_D0_RDY;

    IPM_STATUS_T status;
    status = IPM_NotifyReadyForProcState(ipm_client_id, dwProcState, 1000);
        
    if (status != IPM_STATUS_SUCCESS)
        NKDbgPrintfW(L"camera IPM_NotifyReadyForProcState failed!\r\n");

    if (ipm_block_ref_count)
        status = IPM_SetFreqChangeBlock(ipm_client_id, 1000);
    else
        status = IPM_ReleaseFreqChangeBlock(ipm_client_id, 1000);

    if (status != IPM_STATUS_SUCCESS)
        NKDbgPrintfW(L"camera IPM_SetFreqChangeBlock failed!\r\n");  
}

void CCameraPdd::power_on()
{

    qci_clock_enable(1);
    qci_set_interface(sensor->get_qci_interface());
    sensor->set_power_mode(1);
    Sleep(1);
}

bool CCameraPdd::detect_sensor()
{
    set_ipm_block(1);
    power_on();

    qci_enable();
    Sleep(150);
    bool detected = sensor->detect();
    if (detected)
        sensor->stop_capture();
    is_sensor_on = 1;

    set_ipm_block(0);
    return detected;
}

bool CCameraPdd::detect_disable_sensor5623(Sensor *pSensor) {
    // Set current sensor as ov5623
    Sensor *oldSensor = sensor;
    sensor = pSensor;
    
    power_on();

    qci_enable();
    Sleep(150);
    bool detected = sensor->detect();
    if (detected) {
        RETAILMSG(1, (TEXT("Sensor 5623 is detected.  We need set it disable to avoid conflict with ov7670.\r\n")));
        Ov5623 *pSensor5623 = (Ov5623*)sensor;
        pSensor5623->set_disable();
    }
    
    // Set current sensor back to ov7670.
    sensor = oldSensor;
    return true;
}

void CCameraPdd::power_off()
{
    qci_stop_capture();
    sensor->set_power_mode(0);
    qci_clock_enable(0);
    is_sensor_on = 0;
}
 
void CCameraPdd::set_capture_format(int sensor_format,
                                    FrameSize sensor_size,
                                    format_t* qci_format)
{
    qci_master_timing_t timing;

    timing.width = qci_format->width - 1;
    timing.height = qci_format->height - 1;
    if (qci_format->format != sensor_format)
        timing.height++;
    timing.timing = sensor->get_timing();
    sensor->set_frame_format(sensor_format, sensor_size);
    qci_set_master_timing(&timing);
    qci_set_image_format(sensor_format, qci_format->format);
    qci_set_frame_format(qci_format);
}

void CCameraPdd::start_capture(ULONG mode)
{
    set_ipm_block(1);
    PCS_VIDEOINFOHEADER video_info;

    video_info = &m_pCurrentFormat[mode].VideoInfoHeader;
    UINT width        = video_info->bmiHeader.biWidth;
    UINT height       = video_info->bmiHeader.biHeight;

    qci_image_proc_cfg_t qci_cfg;
    qci_image_proc_cfg_t* qci_cfg_ptr = 0;

    float *coe;

    if (!is_sensor_on)
        detect_sensor();

    camera_cfg = sensor->get_camera_cfg(video_info, mode);

    set_capture_format(camera_cfg->sensor_format,
                       camera_cfg->sensor_size,
                       &camera_cfg->qci_format);

    // coefficent for color conversion. 
    if (camera_cfg->color_cfg)
    {
        coe = camera_cfg->color_cfg->color_correct_coe;
        qci_cfg.coe.k00 = QCI_COE_CONVERT(0.299 * coe[0] + 0.587 * coe[3] + 0.114 * coe[6]);
        qci_cfg.coe.k01 = QCI_COE_CONVERT(0.299 * coe[1] + 0.587 * coe[4] + 0.114 * coe[7]);
        qci_cfg.coe.k02 = QCI_COE_CONVERT(0.299 * coe[2] + 0.587 * coe[5] + 0.114 * coe[8]);

        qci_cfg.coe.k10 = QCI_COE_CONVERT(-0.16874 * coe[0] -0.33126 * coe[3] + 0.5 * coe[6]);
        qci_cfg.coe.k11 = QCI_COE_CONVERT(-0.16874 * coe[1] -0.33126 * coe[4] + 0.5 * coe[7]);
        qci_cfg.coe.k12 = QCI_COE_CONVERT(-0.16874 * coe[2] -0.33126 * coe[5] + 0.5 * coe[8]);

        qci_cfg.coe.k20 = QCI_COE_CONVERT(0.5 * coe[0] -0.41869 * coe[3] - 0.08131 * coe[6]);
        qci_cfg.coe.k21 = QCI_COE_CONVERT(0.5 * coe[1] -0.41869 * coe[4] - 0.08131 * coe[7]);
        qci_cfg.coe.k22 = QCI_COE_CONVERT(0.5 * coe[2] -0.41869 * coe[5] - 0.08131 * coe[8]);

        qci_cfg.scale = 0;
        qci_cfg.black_level = camera_cfg->color_cfg->black_level;
        qci_cfg.lut = camera_cfg->color_cfg->gama;
        qci_cfg_ptr = &qci_cfg;
    }

    // CMU will not be enabled when input is raw data
    if (camera_cfg->qci_format.format != XLLP_CAMERA_IMAGE_FORMAT_RAW10)
        qci_set_image_proc_cfg(qci_cfg_ptr);

#define DEFAULT_STILL_SKIPS 2
    qci_start_capture(mode == STILL? DEFAULT_STILL_SKIPS : 0);
    sensor->start_capture();
}

void CCameraPdd::stop_capture()
{
    sensor->stop_capture();
    qci_stop_capture();
    set_ipm_block(0);
}

const unsigned int color_proc_max_width = 1280;
const unsigned int color_proc_max_height = 1024;

void fill_plane(PUCHAR dest, plane_t* plane, unsigned int size)
{
    // NKDbgPrintfW(L"fill plane dest 0x%08x, src 0x%08x, size 0x%08x", dest, plane, size);
    memcpy(dest, plane->buf.buf, plane->buf.size);
}

void fill_plane_cb_cr(PUCHAR dest, PUCHAR src, 
                      unsigned int width, unsigned int height)
{
    for (unsigned int i = 0; i < height; i++)
    {
        memcpy(dest, src, width);
        dest += width;
        src += width << 1;
    }
}

void fill_buffer_yv12(PUCHAR* dest_plane, frame_t* frame)
{
    format_t *format = &frame->list->format;
    unsigned int y_size = format->width * format->height;
    
    fill_plane(dest_plane[0], &frame->plane[0], y_size);
    fill_plane_cb_cr(dest_plane[2], (PUCHAR)frame->plane[1].buf.buf, 
        format->width >> 1, format->height >> 1);
    fill_plane_cb_cr(dest_plane[1], (PUCHAR)frame->plane[2].buf.buf, 
        format->width >> 1, format->height >> 1);
}

#define CROP_RGGB
void convert_rggb10_rgb565(unsigned short *dst, unsigned short *src, 
			  unsigned int dst_width, unsigned int dst_height,
			  unsigned int src_width, unsigned int src_height)
{
	unsigned int src_x0_pos = 0;
	unsigned int src_pos = 0;

	unsigned int line_step = src_height / dst_height / 2;
	line_step *= 2;
	unsigned int col_step = src_width / dst_width / 2;
	col_step *= 2;
	unsigned int src_size = src_width * src_height;

#ifdef CROP_RGGB
	line_step = 2;
	col_step = 2;
#endif

	unsigned int line, col;

	for (line = 0; line < dst_height && (src_x0_pos + src_width) < src_size; line++)
	{
		for (col = 0; col < dst_width && (src_pos + 1 - src_x0_pos) < src_width; col++)
		{
			unsigned short r = src[src_pos];
			unsigned short g = (src[src_pos + 1] + src[src_pos + src_width]) >> 1;
			unsigned short b = src[src_pos + src_width + 1];

			dst[line * dst_width + col] = ((r >> 5) << 11) | ((g >> 4) << 5) | (b >> 5);
			src_pos += col_step;
		}
		src_x0_pos += src_width * line_step;
		src_pos = src_x0_pos;
	}
}


#ifndef _CALLBACK
#define _CALLBACK __STDCALL
#endif

void* _CALLBACK ippiMalloc(int size)
{
    return malloc(size);
}

void _CALLBACK ippiFree(void* pSrcBuf)
{
    free(pSrcBuf);
}

//function :void set_flashlight(bool is_on)
//when is_0n==TRUE,turn on FlashLight;
//        is_0n==FALSE,turn off FlashLight;
//Note:NO turning on the FlashLight for long time. 

void CCameraPdd::set_flashlight(bool is_on)
{
    XLLP_UINT8_T buffer[1];
    int status;
    const UCHAR GPX_ADDR= 0x66;
    const UCHAR GPX_LIGHT_POWER_ON_MASK = 0x40;
	
	status = OS_I2CMasterReadData(GPX_ADDR, buffer, 1);
    if (XLLP_STATUS_SUCCESS != status) {
        RETAILMSG(1, (TEXT("CCameraPdd:set_flashlight() Failed to read from GPX address.\r\n")));
    } else {
        RETAILMSG(1, (TEXT("CCameraPdd:set_flashlight() GPX value = 0x%02X.\r\n"), buffer[0]));
    }
    
    if (is_on) 
    {
        // Set CIF_PWDN to zero.
        buffer[0] &= ~GPX_LIGHT_POWER_ON_MASK;  
		status = OS_I2CMasterWriteData(GPX_ADDR, buffer, 1);
        if (XLLP_STATUS_SUCCESS != status) 
        {
            RETAILMSG(1, (TEXT("CCameraPdd:set_flashlight() Failed to write GPX address.\r\n")));
        }  
	} 
    else 
    {
        // Set CIF_PWDN to 1.
        buffer[0] |= GPX_LIGHT_POWER_ON_MASK; 
        status = OS_I2CMasterWriteData(GPX_ADDR, buffer, 1);
		if (XLLP_STATUS_SUCCESS != status) {
            RETAILMSG(1, (TEXT("CCameraPdd:set_flashlight() Failed to write GPX address.\r\n")));
		}   
    }
}
